﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;
using System.Net;
using System.IO;
using System.Timers;
using PV.DAL;

namespace PVMonMVC.Objects
{
    public class PVSystemMonitor : IPVSystemMonitor
    {
        string baseUri;
        Timer pvQueryTimer = new Timer();
        Timer dbSaveTimer = new Timer();
        static PVEntitiesContainer pvEntitiesContainer = new PVEntitiesContainer();
        
        public int PVQueryTimerIntervalInSeconds
        {
            get { return (int)(pvQueryTimer.Interval/1000); }
            set 
            {
                if (value < 5)
                {
                    pvQueryTimer.Interval = 5 * 1000;
                }
                else
                {
                    pvQueryTimer.Interval = value * 1000;
                }
            }
        }
        public int DBSaveTimerIntervalInSeconds
        {
            get { return (int)(dbSaveTimer.Interval / 1000); }
            set { dbSaveTimer.Interval = value * 1000; }
        }     
        public PVInverterParams PVparams;
        public PVState State;
        public PVstatistics Statistics; 
        public DateTime RunningSince;
        public int PVSystemStatusUpdates;

        public PVSystemMonitor():this("","")
        {
            
        }
        public PVSystemMonitor(string pVbaseURI, string pvSystemType)
        {
            switch (pvSystemType)
            {
                case "Piko":
                    PVparams=initializeForPiko();
                    break;
                default:
                    throw new Exception("Unkown inverter type:"+pvSystemType);
            }
            if (pVbaseURI != "")
            {
                baseUri = pVbaseURI;
            }
            else
            {
                throw new Exception("No PV system URL given.");
            }
            State = new PVState();
            Statistics = new PVstatistics();
            PVQueryTimerIntervalInSeconds = 5;
            DBSaveTimerIntervalInSeconds = 5;
            dbSaveTimer.Elapsed += new ElapsedEventHandler(dbSaveTimer_Elapsed);
            pvQueryTimer.Elapsed += new ElapsedEventHandler(pvQueryTimer_Elapsed);
            RunningSince = DateTime.Now;
            PVSystemStatusUpdates = 0;
            State = GetCurrentPVSystemState();
        }

        PVInverterParams initializeForPiko()
        {
            XPaths PikoXPaths = new XPaths(
    "//html[1]//body[1]//table[2]//tr[8]//td[3]",  //Status
    "//html[1]//body[1]//table[2]//tr[4]//td[3]",  //AC power
    "//html[1]//body[1]//table[2]//tr[4]//td[6]",  //EnegryTotal
    "//html[1]//body[1]//table[2]//tr[6]//td[6]",  //EnergyDaily
    "//html[1]//body[1]//table[2]//tr[14]//td[3]", "//html[1]//body[1]//table[2]//tr[16]//td[3]", //s1v, s1c ok
    "//html[1]//body[1]//table[2]//tr[19]//td[3]", "//html[1]//body[1]//table[2]//tr[21]//td[3]", //s2v, s2c ...
    "//html[1]//body[1]//table[2]//tr[24]//td[3]", "//html[1]//body[1]//table[2]//tr[26]//td[3]", //s3v, s3c ...
    "//html[1]//body[1]//table[2]//tr[14]//td[6]", "//html[1]//body[1]//table[2]//tr[16]//td[6]", //l1v, l1c ok
    "//html[1]//body[1]//table[2]//tr[19]//td[6]", "//html[1]//body[1]//table[2]//tr[21]//td[6]", //
    "//html[1]//body[1]//table[2]//tr[24]//td[6]", "//html[1]//body[1]//table[2]//tr[26]//td[6]");//

            return new PVInverterParams("Piko10.1", "90342KB40001C", "", "pvserver", "pvwr", PikoXPaths);
        }
        string requestPVStatusPage()
        {
            Uri pvUri = new Uri("http://" + baseUri + "/" + PVparams.PVpageName);
            string responseData = String.Empty;
            HttpWebRequest req = null;
            StreamReader streamReader = null;

            req = (HttpWebRequest)HttpWebRequest.Create(pvUri);
            NetworkCredential nc = new NetworkCredential("pvserver", "pvwr");
            req.Credentials = nc;
            req.Method = WebRequestMethods.Http.Get;
            req.PreAuthenticate = true;
            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
            streamReader = new StreamReader(resp.GetResponseStream());
            responseData = streamReader.ReadToEnd().Trim();

            return responseData;
            //ParsePVStatusPage(responseData); 
            //TODO: make RequestPVstatusPage async
            //CredentialCache myCredentials = new CredentialCache();
            //myCredentials.Add( pvUri,"Basic", new NetworkCredential(Username, Password ));
            //WebClient client = new WebClient();
            //client.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("pvserver:pvwr")));
            //client.Credentials = myCredentials;
            //client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
            //client.Credentials = new NetworkCredential(Username, Password);
            //client.UseDefaultCredentials = false;
            //client.DownloadStringAsync(pvUri);
        }
        PVState parsePVStatusPage(string PageText)
        {
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(PageText);
            // double.Parse( doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.ACPowerXpath).InnerText);
            //string acp= ACPowerNode.InnerText;
            //List<string> listOfCells = new List<string>();
            //listOfCells.Add(row2col1.InnerText);
            State.EnergyTotal = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.TotalEnergyXpath).InnerText.Split().Last<string>());
            State.EnergyDaily = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.DailyEnergyXpath).InnerText.Split().Last<string>());
            string strStatus = doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.StatusXPath).InnerText.Split().Last<String>();
            switch (strStatus)
            {
                case "MPP":
                    //string s1vs = doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String1VoltageXpath).InnerText;
                    State.Status = "On";
                    PVQueryTimerIntervalInSeconds = 60;
                    State.ACPower = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.ACPowerXpath).InnerText);
                    //string str1V = doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String1VoltageXpath).InnerText; //TODO also parse measurements
                    State.String1Voltage = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String1VoltageXpath).InnerText.Split().Last<String>());
                    State.String1Current = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String1CurrentXpath).InnerText.Split().Last<String>());
                    State.String2Voltage = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String2VoltageXpath).InnerText.Split().Last<String>());
                    State.String2Current = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String2CurrentXpath).InnerText.Split().Last<String>());
                    State.String3Voltage = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String3VoltageXpath).InnerText.Split().Last<String>());
                    State.String3Current = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.String3CurrentXpath).InnerText.Split().Last<String>());
                    State.Line1Voltage = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.Line1VoltageXpath).InnerText.Split().Last<String>());
                    State.Line1Current = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.Line1CurrentXpath).InnerText.Split().Last<String>());
                    State.Line2Voltage = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.Line2VoltageXpath).InnerText.Split().Last<String>());
                    State.Line2Current = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.Line2CurrentXpath).InnerText.Split().Last<String>());
                    State.Line3Voltage = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.Line3VoltageXpath).InnerText.Split().Last<String>());
                    State.Line3Current = double.Parse(doc.DocumentNode.SelectSingleNode(PVparams.Xpaths.Line3CurrentXpath).InnerText.Split().Last<String>());
                    break;
                case "off":
                    State.Status = "Off";
                    PVQueryTimerIntervalInSeconds = 600;
                    State.ACPower = 0;
                    State.String1Voltage = 0;
                    State.String1Current = 0;
                    State.String2Voltage = 0;
                    State.String2Current = 0;
                    State.String3Voltage = 0;
                    State.String3Current = 0;
                    State.Line1Voltage = 0;
                    State.Line1Current = 0;
                    State.Line2Voltage = 0;
                    State.Line2Current = 0;
                    State.Line3Voltage = 0;
                    State.Line3Current = 0;
                    break;
                default:
                    State.Status = "Unrecognized status";
                    break;
            }
            return State;
        }
        public PVState convertPVStatusLog2PVstatus(PVStatusLog pvsl)
        {
            PVState pvs = new PVState();
            pvs.TimeStamp = pvsl.TimeStamp;
            pvs.ACPower = pvsl.CurrentPowerKW;
            pvs.EnergyDaily = pvsl.EnergyDailyKWh;
            pvs.EnergyTotal = pvsl.EnergyTotalKWh;
            pvs.String1Current = pvsl.String1Current;
            pvs.String2Current = pvsl.String2Current;
            pvs.String3Current = pvsl.String3Current;
            pvs.String1Voltage = pvsl.String1Voltage;
            pvs.String2Voltage = pvsl.String2Voltage;
            pvs.String3Voltage = pvsl.String3Voltage;
            //pvs.Line1Current = pvsl._ TODO
            return pvs;
        }
        void copyPVStateToEntityAndSaveToDB(PVState PVstate)
        {
            PVStatusLog PVlogEntry = new PVStatusLog();
            PVlogEntry.TimeStamp = DateTime.Now;
            PVlogEntry.Status = PVstate.Status;
            PVlogEntry.CurrentPowerKW = PVstate.ACPower;
            PVlogEntry.EnergyDailyKWh = PVstate.EnergyDaily;
            PVlogEntry.EnergyTotalKWh = PVstate.EnergyTotal;
            PVlogEntry.String1Voltage = PVstate.String1Voltage;
            PVlogEntry.String2Voltage = PVstate.String2Voltage;
            PVlogEntry.String3Voltage = PVstate.String3Voltage;
            PVlogEntry.String1Current = PVstate.String1Current;
            PVlogEntry.String2Current = PVstate.String2Current;
            PVlogEntry.String3Current = PVstate.String3Current;
            pvEntitiesContainer.PVStatusLogs.AddObject(PVlogEntry);
            pvEntitiesContainer.SaveChanges();
        }
        void pvQueryTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            State = GetCurrentPVSystemState();         
        }
        void dbSaveTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (State != null)
            {
                copyPVStateToEntityAndSaveToDB(State);
                PVSystemStatusUpdates = PVSystemStatusUpdates+1;
            } 
        }

        public PVState GetCurrentPVSystemState()
        {
            string inverterHtmlResponse = requestPVStatusPage();
            PVState state = parsePVStatusPage(inverterHtmlResponse);
            return state;
        }
        public void StartQueryingPVStatusPeriodically()
        {
            pvQueryTimer.Start();
        }
        public void StopQueryingPVStatusPeriodically()
        {
            pvQueryTimer.Stop();
        }
        public void StartSavingToDBPeriodically()
        {
            dbSaveTimer.Start();
        }
        public void StopSavingToDBPeriodically()
        {
            dbSaveTimer.Stop();
        }

        public List<PVState> GetPVSystemStateForPeriod(DateTime start, DateTime end)
        {
            List<PVState> log = new List<PVState>();
            return log;
        }

        //void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        //{
        //    if (e.Error == null)
        //        parsePVStatusPage(e.Result.ToString());
        //    //else
        //    // = e.Error.Message;
        //}
    }
}
